var defaultSettings = {
                'from': '',
                'to': '',
                'session': 1,
                'currentWord': 0,
                'questionType': 0,
                'initialLearningChunkSize': 50,
                'promotionLimit': 5, // how often has a word to be guessed correctly to be promoted to box 4
                'promotedWords': 0
               },
    settings = defaultSettings,
    boxIndices = [[]],
    sessionIndices = [],
    optionListIndices = [];

installClickHandlers();
if (verifyDictionary()) {
    loadState();
} else {
    showStatus("Your dictionary is not valid");
}

if (typeof String.prototype.startsWith != 'function') {
  String.prototype.startsWith = function (str){
    return this.slice(0, str.length) === str;
  };
}

function randomInt( maxValue ) {
    return Math.floor(Math.random() * maxValue);
}

function toBase64(str) {
    return window.btoa(unescape(encodeURIComponent(str)));
}

function fromBase64(str) {
    return decodeURIComponent(escape(window.atob(str)));
}

function shuffle( arr ) {
    var j, tempi, tempj, i = arr.length;
    if (i === 0) return arr;
    while ( --i) {
        j = randomInt(i+1);
        tempi = arr[i];
        tempj = arr[j];
        arr[i] = tempj;
        arr[j] = tempi;
    }
    return arr;
}

function remove( arr, item ) {
    var foundAt = -1;
    $.each(arr, function(index, value) {
        if (value['id'] === item['id']) {
            foundAt = index;
            return false;
        }
    });
    if (foundAt >= 0) {
        arr.splice(foundAt, 1);
    }
}

function exists( arr, item ) {
    var existing = false;
    $.each(arr, function(index, value) {
        if (value['id'] === item['id']) {
            existing = true;
            return false;
        }
    });
    return existing;
}

function sortByGuessesCorrectly( arr ) {
    arr.sort(function(a, b) {
        if (a.guessedCorrectly < b.guessedCorrectly) {
            return -1;
        }
        if (a.guessedCorrectly > b.guessedCorrectly) {
            return 1;
        }
        return 0;
    });

    return arr;
}

function get( arr, item ) {
    var entry = null;
    $.each(arr, function(index, value) {
        if (value['id'] === item['id']) {
            entry = value;
            return false;
        }
    });
    return entry;
}

function createStartingBox() {
    var indicesFromDictionary = [];
    shuffle(dictionary);
    $.each(dictionary, function(index, value) {
        indicesFromDictionary.push({id:value['id'], guessedCorrectly:0});
    });
    return indicesFromDictionary.splice(0, Math.min(settings.initialLearningChunkSize, indicesFromDictionary.length));
}

function addNewWordFromDictionary() {
    var notInBox1, notInBox2, notInBox3, notInBox4, enoughWordsAlready;
    if (settings.promotedWords === 0) {
        return;
    }
    enoughWordsAlready =(boxIndices[1].length + boxIndices[2].length + boxIndices[3].length) >= settings.initialLearningChunkSize;
    if (enoughWordsAlready) {
        return;
    }
    shuffle(dictionary);
    $.each(dictionary, function(index, value) {
        notInBox1 = !exists(boxIndices[1], value);
        notInBox2 = !exists(boxIndices[2], value);
        notInBox3 = !exists(boxIndices[3], value);
        notInBox4 = !exists(boxIndices[4], value);
        if (notInBox1 && notInBox2 && notInBox3 && notInBox4) {
            boxIndices[1].push({id:value['id'], guessedCorrectly:0});
            enoughWordsAlready =(boxIndices[1].length + boxIndices[2].length + boxIndices[3].length) >= settings.initialLearningChunkSize;
            settings['promotedWords']--;
            if (enoughWordsAlready || settings['promotedWords'] === 0) {
                return false;
            }
        }
    });
}

function transformToIndicesOnly( arr ) {
    var result = [];
    $.each(arr, function(index, value) {
        result.push(value['id']);
    });
    return result;
}

function transformToDictionaryEntries( indexedArray ) {
    var result = [];
    $.each(indexedArray, function(index, value) {
        result.push(getDictionaryEntryById(value));
    });
    return result;
}

function getDictionaryEntryById( id ) {
    var dictionaryEntry = null;
    $.each(dictionary, function(index, value) {
        if (value['id'] === id) {
            dictionaryEntry = value;
            return false;
        }
    });
    return dictionaryEntry;
}

function initGame() {
    settings = defaultSettings;
    boxIndices[1] = createStartingBox();
    boxIndices[2] = [];
    boxIndices[3] = [];
    boxIndices[4] = [];
    sessionIndices = [];
    optionListIndices = [];
}

function checkGameComplete() {
    if (boxIndices[1].length === 0 && boxIndices[2].length === 0 && boxIndices[3].length === 0) {
        showStatus("It is time to add new words to the dictionary!");
        return true;
    }
    return false;
}

function prepareNextSession() {
    var maxIndex = 0;
    addNewWordFromDictionary();
    if (!checkGameComplete()) {
        sessionIndices = shuffle(boxIndices[1].slice());
        if (settings.session === 2) {
            sessionIndices = sessionIndices.concat(shuffle(boxIndices[2].slice()));
        }
        if (settings.session === 3) {
            sessionIndices = sessionIndices.concat(shuffle(boxIndices[2].slice()));
            sessionIndices = sessionIndices.concat(shuffle(boxIndices[3].slice()));
        }
        if (sessionIndices.length == 0) {
            settings.session++;
            if (settings.session > 3) {
                settings.session = 1;
            }
            prepareNextSession();
        } else {
            if (randomInt(3) == 0 && boxIndices[4].length > 0) {
                // add up to two random perfectly known words
                maxIndex = Math.min(boxIndices[4].length, 2);
                sessionIndices = sessionIndices.concat(shuffle(boxIndices[4]).slice(0, maxIndex));
                // shuffle session
                shuffle(sessionIndices);
            }
        }
    }
}

function showNextWord() {
    var session = transformToDictionaryEntries(transformToIndicesOnly(sessionIndices)),
        newWord = session[settings.currentWord];

    $('#wordToGuess').text(newWord[settings.from]);

    // calculate new question type
    calculateQuestionType();

    if (settings.questionType === 1) {
        showMultipleChoiceInput();
    } else if (settings.questionType === 2) {
        showWordInput();
    } else if (settings.questionType === 3) {
        showMultipleChoiceInputWithArticleSelection();
    }

    updateButtonStates();
    updateInfoPanel();
    saveProgress();
    settings.questionType = 0;
}

function calculateQuestionType() {
    var session = transformToDictionaryEntries(transformToIndicesOnly(sessionIndices)),
        nextWord = session[settings.currentWord],
        nextWordEntry = sessionIndices[settings.currentWord];

    if (settings.questionType == 0) {
        settings.questionType = 1;
        if (exists(boxIndices[4], nextWordEntry)) {
            settings.questionType = 2;
        } else if (exists(boxIndices[3], nextWordEntry)) {
            settings.questionType = 2;
        } else if (exists(boxIndices[2], nextWordEntry) && randomInt(5) == 0) {
            settings.questionType = 2;
        } else if (settings.to === 'German' && nextWord.type === 'noun') {
            settings.questionType = 3;
        }
    }
}

function showWordInput() {
    $('#multipleChoice').hide();
    $('#multipleChoiceWithArticles').hide();
    $('#singleInput').show();
    $('#guessedInput').focus();
    if (settings.from === 'German') {
        $('#inputHelpDE').show();
        $('#inputHelpHR').hide();
    } else {
        $('#inputHelpDE').hide();
        $('#inputHelpHR').show();
    }
}

function createRandomOptionListIndices() {
var randomOptionId, possibleOption, wordsHaveSameType,
    session = transformToDictionaryEntries(transformToIndicesOnly(sessionIndices)),
    nextWord = session[settings.currentWord],
    nextWordIndex = sessionIndices[settings.currentWord].id;

    // build options array
    if (optionListIndices.length < 4) {
        optionListIndices.push(nextWordIndex);
    }
    while (optionListIndices.length < 4) {
        randomOptionId = dictionary[randomInt(dictionary.length)].id;
        possibleOption = getDictionaryEntryById(randomOptionId);
        wordsHaveSameType = possibleOption.type === nextWord.type;
        if ($.inArray(possibleOption.id, optionListIndices) == -1 && wordsHaveSameType) {
            optionListIndices.push(randomOptionId);
        }
    }
    shuffle(optionListIndices);

}

function showMultipleChoiceInput() {
    var optionList;
    $('#singleInput').hide();
    $('#multipleChoiceWithArticles').hide();
    $('#multipleChoice').show();
    createRandomOptionListIndices();
    // fill options
    optionList = transformToDictionaryEntries(optionListIndices);
    $.each(optionList, function(index, value) {
        $('#option'+(index + 1)).text(value[settings.to]);
    });
}

function showMultipleChoiceInputWithArticleSelection() {
    var optionList, optionValue;

    $('#singleInput').hide();
    $('#multipleChoice').hide();
    $('#multipleChoiceWithArticles').show();

    createRandomOptionListIndices();

    // fill options
    optionList = transformToDictionaryEntries(optionListIndices);
    $.each(optionList, function(index, value) {
        optionValue = value[settings.to].substring(4);

        $('#wordWithoutArticle'+(index + 1)).text(optionValue);
    });
}

function updateInfoPanel() {
    var wordStr, percentageStr;

    percentageStr = ((settings.currentWord + 1) / sessionIndices.length * 100).toFixed(2) + '% ';
    wordStr = '(' + (settings.currentWord + 1) + '/' + sessionIndices.length + ')';

    $('#infoSessionValue').text(settings.session);
    $('#infoProgressValue').text(percentageStr + wordStr);
    $('#infoUnknownValue').text(boxIndices[1].length);
    $('#infoKnownValue').text(boxIndices[2].length);
    $('#infoWellKnownValue').text(boxIndices[3].length);
    $('#infoPerfectlyKnownValue').text(boxIndices[4].length);
}

function checkWord( guessedWord ) {
    var wordToGuess = getDictionaryEntryById(transformToIndicesOnly(sessionIndices)[settings.currentWord]),
        currentEntry = sessionIndices[settings.currentWord];

    guessedWord = guessedWord.trim();
    $('#hint').text(getHintText(wordToGuess));
    if (guessedWord === wordToGuess[settings.to]) {
        $('#result').addClass('correct').removeClass('wrong').text('\u2714');
        if (exists(boxIndices[4], currentEntry)) { // box 4
            currentEntry = get(boxIndices[4], currentEntry);
            currentEntry.guessedCorrectly++;
        }
        if (exists(boxIndices[3], currentEntry)) { // box 3
            currentEntry = get(boxIndices[3], currentEntry);
            currentEntry.guessedCorrectly++;
            if (currentEntry.guessedCorrectly >= settings.promotionLimit) {
                remove(boxIndices[3], currentEntry);
                boxIndices[4].push(currentEntry);
                settings.promotedWords++;
                showStatus("The word is perfectly known now.");
            } else {
                showStatus(settings.promotionLimit - currentEntry.guessedCorrectly + ' more to go');
            }
        }
        handleCorrectWord(currentEntry, 2); // box 2
        handleCorrectWord(currentEntry, 1); // box 1
    } else {
        logWords(guessedWord, wordToGuess[settings.to]);
        $('#result').addClass('wrong').removeClass('correct').text('\u2718');
        handleWrongWord(currentEntry, 4); // box 4
        handleWrongWord(currentEntry, 3); // box 3
        handleWrongWord(currentEntry, 2); // box 2
    }
}

function progressInGame() {
    settings.currentWord++;
    if (settings.currentWord >= sessionIndices.length) {
        settings.session++;
        if (settings.session > 3) {
            settings.session = 1;
        }
        settings.currentWord = 0;
        prepareNextSession();
    }
}

function handleWrongWord( currentEntry, boxNumber ) {
    if (exists(boxIndices[boxNumber], currentEntry)) {
        remove(boxIndices[boxNumber], currentEntry);
        currentEntry.guessedCorrectly = 0;
        boxIndices[1].push(currentEntry);
    }
}

function handleCorrectWord( currentEntry, boxNumber ) {
    if (exists(boxIndices[boxNumber], currentEntry)) {
        remove(boxIndices[boxNumber], currentEntry);
        boxIndices[boxNumber + 1].push(currentEntry);
    }
}

function getHintText( wordToGuess ) {
    var hintText = wordToGuess[settings.from];
    if (settings.from === 'Croatian') {
        hintText += ' ' + wordToGuess.hint + ' = ' + wordToGuess[settings.to];
    } else {
        hintText += ' = ' + wordToGuess[settings.to] + ' ' + wordToGuess.hint;
    }
    return hintText;
}

function showStatus(msg) {
    $('#statusText').show();
    $('#statusText').text(msg);
    $('#statusText').fadeOut(2000);
}

function updateButtonStates() {
    $('#dehr').prop('disabled', settings.from === 'German');
    $('#hrde').prop('disabled', settings.from === 'Croatian');
    if (boxIndices[4].length > 10) {
        $('#playPerfectlyKnownWordsGame').show();
    } else {
        $('#playPerfectlyKnownWordsGame').hide();
    }
}

function enableButtons() {
    $('#dehr').prop('disabled', false);
    $('#hrde').prop('disabled', false);
}

function loadState() {
    var storedBox1 = localStorage.getItem('savedBox1'),
        storedBox2 = localStorage.getItem('savedBox2'),
        storedBox3 = localStorage.getItem('savedBox3'),
        storedBox4 = localStorage.getItem('savedBox4'),
        storedSession = localStorage.getItem('savedSession'),
        storedSettings = localStorage.getItem('savedSettings'),
        storedOptionList = localStorage.getItem('savedOptionList');

    boxIndices[1] = JSON.parse(storedBox1);
    boxIndices[2] = JSON.parse(storedBox2);
    boxIndices[3] = JSON.parse(storedBox3);
    boxIndices[4] = JSON.parse(storedBox4);
    sessionIndices = JSON.parse(storedSession);
    settings = JSON.parse(storedSettings);
    optionListIndices = JSON.parse(storedOptionList);

    if (settings === null || settings === undefined) {
        initGame();
        prepareNextSession();
    } else {
        showNextWord();
        if (checkGameComplete()) {
            addNewWordFromDictionary();
        }
    }
}

function saveProgress() {
    localStorage.setItem('savedBox1', JSON.stringify(boxIndices[1]));
    localStorage.setItem('savedBox2', JSON.stringify(boxIndices[2]));
    localStorage.setItem('savedBox3', JSON.stringify(boxIndices[3]));
    localStorage.setItem('savedBox4', JSON.stringify(boxIndices[4]));
    localStorage.setItem('savedSession', JSON.stringify(sessionIndices));
    localStorage.setItem('savedSettings', JSON.stringify(settings));
    localStorage.setItem('savedOptionList', JSON.stringify(optionListIndices));
};

function installClickHandlers() {

    $('#deleteState').click(function(){
        var result = confirm("Do you really want to delete your progress?");
        if (result === true) {
            localStorage.removeItem('savedBox1');
            localStorage.removeItem('savedBox2');
            localStorage.removeItem('savedBox3');
            localStorage.removeItem('savedBox4');
            localStorage.removeItem('savedSession');
            localStorage.removeItem('savedSettings');
            localStorage.removeItem('savedOptionList');

            initGame();
            prepareNextSession();
            $('#singleInput').hide();
            $('#multipleChoice').hide();
            $('#multipleChoiceWithArticles').hide();
            $('#exportPanel').hide();
            $('#importPanel').hide();
            $('#wordToGuess').text('');
            $('#result').text('');
            $('#hint').text('');
            $('#infoPanel').text('');
            enableButtons();
            showStatus('Deleted state');
        }
    });

    $('#dehr').click(function(){
        settings.from = 'German';
        settings.to = 'Croatian';
        optionListIndices = [];
        showNextWord();
    });

    $('#hrde').click(function(){
        settings.from = 'Croatian';
        settings.to = 'German';
        optionListIndices = [];
        showNextWord();
    });

    $('#exportState').click(function(){
        var exportedData = toBase64(JSON.stringify(boxIndices[1]));
        exportedData += "#" + toBase64(JSON.stringify(boxIndices[2]));
        exportedData += "#" + toBase64(JSON.stringify(boxIndices[3]));
        exportedData += "#" + toBase64(JSON.stringify(boxIndices[4]));
        exportedData += "#" + toBase64(JSON.stringify(sessionIndices));
        exportedData += "#" + toBase64(JSON.stringify(settings));
        exportedData += "#" + toBase64(JSON.stringify(optionListIndices));

        $('#exportField').val(exportedData);
        $('#exportPanel').show();
        $('#importPanel').hide();
    });

    $('#exportClose').click(function() {
        $('#exportPanel').hide();
        $('#exportField').val('');
    });

    $('#importState').click(function() {
        $('#importPanel').show();
        $('#exportPanel').hide();
    });

    $('#importStart').click(function() {
        var importedData = $('#importField').val();
        importedDataArray = importedData.split('#');
        if (importedDataArray.length === 7) {
            boxIndices[1] = JSON.parse(fromBase64(importedDataArray[0]));
            boxIndices[2] = JSON.parse(fromBase64(importedDataArray[1]));
            boxIndices[3] = JSON.parse(fromBase64(importedDataArray[2]));
            boxIndices[4] = JSON.parse(fromBase64(importedDataArray[3]));
            sessionIndices = JSON.parse(fromBase64(importedDataArray[4]));
            settings = JSON.parse(fromBase64(importedDataArray[5]));
            optionListIndices = JSON.parse(fromBase64(importedDataArray[6]));
            showNextWord();
        } else {
            showStatus('Invalid save state');
        }
        $('#importPanel').hide();
    });

    $('.inputHelp').click(function() {
        var letter = $(this).val();
        $('#guessedInput').val($('#guessedInput').val() + letter);
        $('#guessedInput').focus();
    });

    $('.options').click(function() {
        checkWord($(this).text());
        progressInGame();
        optionListIndices = [];
        showNextWord();
    });

    $('.articles').click(function() {
        var guessedWord = $(this).text();
        guessedWord += ' ' + $(this).parent().siblings().children('span').text();
        checkWord(guessedWord);
        progressInGame();
        optionListIndices = [];
        showNextWord();
    });

    $('#guessedInput').bind("enterKey",function(e) {
        checkWord($('#guessedInput').val());
        progressInGame();
        $('#guessedInput').val('');
        optionListIndices = [];
        showNextWord();
    });

    $('#guessedInput').keyup(function(e) {
        if(e.keyCode == 13) {
            $(this).trigger("enterKey");
        }
    });

    $('#playPerfectlyKnownWordsGame').click(function() {
        $('#overlay').show();
        showSubGame();
    });

    $('#infoUnknownValue').click(function() {
        $('#overlay').show();
        $('#popup').show().html(getBoxInfo(boxIndices[1]));
    });

    $('#infoKnownValue').click(function() {
        $('#overlay').show();
        $('#popup').show().html(getBoxInfo(boxIndices[2]));
    });

    $('#infoWellKnownValue').click(function() {
        $('#overlay').show();
        $('#popup').show().html(getBoxInfo(boxIndices[3]));
    });

    $('#infoPerfectlyKnownValue').click(function() {
        $('#overlay').show();
        $('#popup').show().html(getBoxInfo(boxIndices[4]));
    });

    $('#overlay').click(function() {
        $('#popup').hide();
        $('#overlay').hide();
    });

}

function showSubGame() {
    var index = 0, subBox;
    subBox = shuffle(boxIndices[4]);
    subBox = sortByGuessesCorrectly(subBox);
    subBox = transformToIndicesOnly(subBox);
    subBox = transformToDictionaryEntries(subBox);
    showSubGameWord(subBox, index);
}

function showSubGameWord( subBox, index ) {
    var inputElement, template, currentEntry;
    template = '<div id="subgame"><p>';
    template += subBox[index][settings.from];
    template += ': <input id="subgameInput" /></p>';
    template += '<p class="subgameInfo">' + index + '/' + subBox.length + '</p></div>';
    $('#popup').html(template).show();
    inputElement = $('#subgameInput');
    inputElement.focus();
    inputElement.bind("enterKey",function(e) {
        if (inputElement.val().trim() === subBox[index][settings.to]) {
            currentEntry = get(boxIndices[4], subBox[index]);
            currentEntry.guessedCorrectly++;
        } else {
            logWords(inputElement.val(), subBox[index][settings.to]);
            handleWrongWord(subBox[index], 4); // box 4
        }
        updateButtonStates();
        updateInfoPanel();
        saveProgress();
        index++;
        if (boxIndices[4].length > 10 && index < subBox.length) {
            showSubGameWord(subBox, index);
        } else {
            $('#popup').hide();
            $('#overlay').hide();
            showStatus("Enough playing for now!");
        }
    });

    inputElement.keyup(function(e) {
        if(e.keyCode == 13) {
            $(this).trigger("enterKey");
        }
    });
}

function logBox( indexedBox, title ) {
    var dictionaryEntry;
    console.log("------------------------[" + title + "]------------------------------------");
    $.each(indexedBox, function(key, value) {
        dictionaryEntry = getDictionaryEntryById(value.id);
        console.log(value.guessedCorrectly, dictionaryEntry.German, dictionaryEntry.Croatian, dictionaryEntry.id);
    });
    console.log("------------------------------------------------------------");
}

function logWords(guessedWord, correctWord) {
    console.log("correct word: [" + correctWord + "]");
    console.log("guessed word: [" + guessedWord + "]");
}

function getBoxInfo( boxArr ) {
    var box = transformToDictionaryEntries(transformToIndicesOnly(boxArr)),
        boxInfo = '';
    box.sort(function(a, b) {
        if (a[settings.from] < b[settings.from]) {
            return -1;
        }
        if (a[settings.from] > b[settings.from]) {
            return 1;
        }
        return 0;
    });
    $.each(box, function(index, value) {
        boxInfo += value[settings.from] + '\n';
    });
    return '<textarea disabled>' + boxInfo + '</textarea>';
}

function verifyDictionary() {
    var isDictionaryValid = verifyDictionaryTypes();

    if (isDictionaryValid) {
        isDictionaryValid = verifyDictionaryDuplicates();
    }

    return isDictionaryValid;
}

function verifyDictionaryTypes() {
    var typeDic = {},
        isDictionaryValid = true;

    $.each(dictionary, function(index, value) {
        if (typeDic[value.type] !== undefined) {
            typeDic[value.type]++;
        } else {
            typeDic[value.type] = 1;
        }
    });

    $.each(typeDic, function(index, value) {
        if (value < 5) {
            console.log("not enough entries for", index);
            isDictionaryValid = false;
        }
    });

    if (typeDic.verb_1s !== typeDic.verb) {
        console.log("number of first person singular verbs doesn't match");
        isDictionaryValid = false;
    }

    if (typeDic.verb_2s !== typeDic.verb) {
        console.log("number of second person singular verbs doesn't match");
        isDictionaryValid = false;
    }

    if (typeDic.verb_3s !== typeDic.verb) {
        console.log("number of third person singular verbs doesn't match");
        isDictionaryValid = false;
    }

    if (typeDic.verb_1p !== typeDic.verb) {
        console.log("number of first person plural verbs doesn't match");
        isDictionaryValid = false;
    }

    if (typeDic.verb_2p !== typeDic.verb) {
        console.log("number of second person plural verbs doesn't match");
        isDictionaryValid = false;
    }

    if (typeDic.verb_3p !== typeDic.verb) {
        console.log("number of third person plural verbs doesn't match");
        isDictionaryValid = false;
    }

    return isDictionaryValid;
}

function verifyDictionaryDuplicates() {
    var duplicateGermanDic = {},
        duplicateCroatianDic = {},
        duplicateIds = {},
        isDictionaryValid = true;

    $.each(dictionary, function(index, value) {
        // prepare German duplicate look up table
        if (duplicateGermanDic[value.German] === undefined) {
            duplicateGermanDic[value.German] = [];
        }
        duplicateGermanDic[value.German].push(value.Croatian);
        // prepare Croatian duplicate look up table
        if (duplicateCroatianDic[value.Croatian] === undefined) {
            duplicateCroatianDic[value.Croatian] = [];
        }
        duplicateCroatianDic[value.Croatian].push(value.German);
        // prepare ID duplicate look up table
        if (duplicateIds[value.id] !== undefined) {
            duplicateIds[value.id]++;
        } else {
            duplicateIds[value.id] = 1;
        }
    });

    // check German words for duplicates
    $.each(duplicateGermanDic, function(index, value) {
        if (value.length > 1) {
            console.log("Found duplicate entries for " + index + " : " + value);
            isDictionaryValid = false;
        }
    });

    // check Croatian words for duplicates
    $.each(duplicateCroatianDic, function(index, value) {
        if (value.length > 1) {
            console.log("Found duplicate entries for " + index + " : " + value);
            isDictionaryValid = false;
        }
    });

    // check for duplicated ids
    $.each(duplicateIds, function(index, value) {
        if (value > 1) {
            console.log("Found duplicate entries for " + index);
            isDictionaryValid = false;
        }

    });

    return isDictionaryValid;
}

function beautifyDictionary() {
   var keys = [],
       newDictionary = [],
       newDictionaryEntry = {},
       newId = 0;

     // get max id
    $.each(dictionary, function(index, value) {
        if (value.id !== undefined && newId < value.id) {
            newId = value.id;
        }
    });
    // normalize keys
    $.each(dictionary, function(index, value) {
        keys = Object.keys(value);
        // adjust type for verbs
        if (value.German.startsWith('ich ')) {
            value.type = 'verb_1s';
        }
        if (value.German.startsWith('du ')) {
            value.type = 'verb_2s';
        }
        if (value.German.startsWith('er ')) {
            value.type = 'verb_3s';
        }
        if (value.German.startsWith('wir ')) {
            value.type = 'verb_1p';
        }
        if (value.German.startsWith('ihr ')) {
            value.type = 'verb_2p';
        }
        if (value.German.startsWith('sie ')) {
            value.type = 'verb_3p';
        }
        // add key 'id' if missing
        if (keys.indexOf('id') === -1) {
            keys.push('id');
        }
        // sort keys alphabetically, but key 'type' first
        keys.sort(function(a, b) {
            if (a === 'type') {
                return -1;
            } else {
                if (a > b) {
                    return 1;
                }
                if (b > a) {
                    return -1;
                }
            }
            return 0;
        });
        newDictionaryEntry = {};
        // assign old id or new calculated id
        $.each(keys, function(keyIndex, keyValue) {
            if (keyValue === 'id' && value[keyValue] === undefined) {
                newDictionaryEntry[keyValue] = ++newId;
            } else {
                newDictionaryEntry[keyValue] = value[keyValue];
            }
        });
        newDictionary.push(newDictionaryEntry);
    });
    // print beautified dictionary
    $.each(newDictionary, function(index, value) {
        console.log(JSON.stringify(value));
    });
    console.log("next usable id:", ++newId);
}
